/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine.cmd.component;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.ScriptRuntimeException;
import cn.easyplatform.contexts.ListContext;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.Record;
import cn.easyplatform.engine.runtime.datalist.DataListUtils;
import cn.easyplatform.interceptor.AbstractCommand;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.UploadRequestMessage;
import cn.easyplatform.messages.response.SimpleResponseMessage;
import cn.easyplatform.messages.vos.FileVo;
import cn.easyplatform.messages.vos.UploadVo;
import cn.easyplatform.messages.vos.datalist.ListUploadVo;
import cn.easyplatform.support.transform.Parameter;
import cn.easyplatform.support.transform.TagNode;
import cn.easyplatform.support.transform.Transformer;
import cn.easyplatform.support.transform.TransformerFactory;
import cn.easyplatform.support.transform.handler.Flating4ListHandler;
import cn.easyplatform.support.transform.handler.FlatingHandler;
import cn.easyplatform.support.transform.handler.StructuringHandler;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.util.MessageUtils;
import cn.easyplatform.util.RuntimeUtils;
import cn.easyplatform.utils.Images;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.io.*;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class UploadCmd extends AbstractCommand<UploadRequestMessage> {

    private static final Logger log = LoggerFactory.getLogger(UploadCmd.class);

    /**
     * @param req
     */
    public UploadCmd(UploadRequestMessage req) {
        super(req);
    }

    @Override
    public IResponseMessage<?> execute(CommandContext cc) {
        UploadVo uv = req.getBody();
        RecordContext rc = null;
        if (uv instanceof ListUploadVo) {
            ListUploadVo luv = (ListUploadVo) uv;
            ListContext lc = cc.getWorkflowContext().getList(luv.getId());
            if (lc == null)
                return MessageUtils.dataListNotFound(luv.getId());
            rc = lc.getRecord(luv.getKeys());
            if (rc == null) {
                if (lc.isCustom()) {
                    FieldDo[] data = DataListUtils.getRow(cc, lc,
                            luv.getKeys());
                    Record record = new Record();
                    for (int i = 0; i < data.length; i++)
                        record.set(data[i]);
                    rc = lc.createRecord(luv.getKeys(), record);
                } else {
                    Record record = DataListUtils.getRecord(cc, lc,
                            luv.getKeys());
                    rc = lc.createRecord(luv.getKeys(), record);
                    lc.appendRecord(rc);
                }
            }
        } else
            rc = cc.getWorkflowContext().getRecord();
        if (log.isDebugEnabled())
            log.debug("upload file {}", uv.getFiles().get(0).getFileName());
        if (uv.getTarget().startsWith("field")) {
            String c = saveField(uv, cc, rc);
            return new SimpleResponseMessage(c);
        } else {
            if (uv.getTarget().startsWith("list")) {
                saveList(uv, cc, rc);
            } else if (!Strings.isBlank(uv.getLogic())) {
                save(uv, cc, rc);
            } else if (!Strings.isBlank(uv.getBackup())) {
                //String[] result = new String[uv.getFiles().size()];
                for (FileVo fv : uv.getFiles()) {
                    String filename = fv.getFileName();
                    if (uv.isObfuscation())
                        filename = RandomStringUtils.random(32, true, true)
                                + "." + FilenameUtils.getExtension(filename);
                    if (!Strings.isBlank(uv.getFilename())) {// 保存文件名称
                        FieldDo fd = rc.getField(uv.getFilename());
                        fd.setValue(filename);
                    }
                    if (uv.getBackup().startsWith("$707") || uv.getBackup().startsWith("$729")) {
                        String path = saveFile(cc, rc, uv, filename,
                                fv.getData());
                        if (!Strings.isBlank(uv.getPath())) {
                            FieldDo fd = rc.getField(uv.getPath());
                            fd.setValue(path);
                        }
                    } else if (!Strings.isBlank(uv.getPath())) {
                        FieldDo fd = rc.getField(uv.getPath());
                        fd.setValue(uv.getBackup());
                    }
                    if (!Strings.isBlank(uv.getAfter()))
                        doAfter(cc, rc, uv.getAfter());
                }
            }
        }
        return new SimpleResponseMessage();
    }

    private void save(UploadVo uv, CommandContext cc, RecordContext rc) {
        FileVo file = uv.getFiles().get(0);
        String ext = FilenameUtils.getExtension(file.getFileName());
        if (!Strings.isBlank(uv.getFormat()))
            ext = uv.getFormat();
        Transformer<Object, ?> transformer = null;
        int type = 0;
        if (ext.equalsIgnoreCase("xml") || ext.equalsIgnoreCase("swi")) {
            type = ext.equalsIgnoreCase("xml") ? Transformer.XML2TABLE
                    : Transformer.SWI2TABLE;
            Transformer<Object, Map<String, List<TagNode>>> trans = TransformerFactory
                    .createTransformer(type);
            trans.setHandler(new StructuringHandler(cc, rc, ext.toUpperCase()));
            transformer = trans;
        } else if (ext.toLowerCase().startsWith("xls")
                || ext.equalsIgnoreCase("et")) {
            type = Transformer.EXCEL2TABLE;
            Transformer<Object, Map<String, List<List<FieldDo>>>> trans = TransformerFactory
                    .createTransformer(type);
            trans.setHandler(new FlatingHandler(cc, rc, uv.getLogic(), uv
                    .getFirstRowNum(), uv.getFirstColNum(), uv.getLastRowNum(),
                    uv.getLastColNum()));
            transformer = trans;
        } else if (ext.equalsIgnoreCase("csv")) {
            type = Transformer.CSV2TABLE;
            Transformer<Object, Map<String, List<List<FieldDo>>>> trans = TransformerFactory
                    .createTransformer(type);
            trans.setHandler(new FlatingHandler(cc, rc, uv.getLogic(), uv
                    .getFirstRowNum(), uv.getFirstColNum(), uv.getLastRowNum(),
                    uv.getLastColNum()));
            transformer = trans;
        }
        InputStream is = null;
        try {
            cc.beginTx();
            for (FileVo fv : uv.getFiles()) {
                if (!Strings.isBlank(uv.getBefore()))
                    doBefore(cc, fv, rc, uv.getBefore());
                is = new ByteArrayInputStream(fv.getData());
                if (type == Transformer.XML2TABLE) {
                    SAXReader reader = new SAXReader();
                    reader.setEncoding(uv.getCharset());
                    transformer.transform(is, null);
                } else if (type == Transformer.SWI2TABLE) {
                    transformer
                            .transform(IOUtils.toString(is, uv.getCharset()), null);
                } else if (type == Transformer.EXCEL2TABLE) {
                    Parameter parameter = new Parameter();
                    if (Strings.isBlank(uv.getSheetNames())) {
                        parameter.setSheets(new String[]{"1"});
                    } else if (!"*".equals(uv.getSheetNames())) {
                        parameter.setSheets(uv.getSheetNames().split(","));
                    }
                    parameter.setColumnsRow(uv.getColumnsRow());
                    parameter.setProgress(uv.isProgress());
                    parameter.setCountOfThread(uv.getCountOfThread());
                    if (ext.equalsIgnoreCase("xls")
                            || ext.equalsIgnoreCase("et"))// 97~2007或wps
                        transformer.transform(
                                new HSSFWorkbook(is), parameter);
                    else
                        // 2010及以上
                        transformer.transform(
                                new XSSFWorkbook(is), parameter);
                } else if (type == Transformer.CSV2TABLE) {
                    Parameter parameter = new Parameter();
                    parameter.setSheets(new String[]{"\r\n", ","});
                    transformer.transform(
                            IOUtils.toString(is, uv.getCharset()), parameter);
                }
                String filename = fv.getFileName();
                if (uv.isObfuscation())
                    filename = RandomStringUtils.random(32, true, true) + "."
                            + FilenameUtils.getExtension(filename);
                if (!Strings.isBlank(uv.getFilename())) {// 保存文件名称
                    FieldDo fd = rc.getField(uv.getFilename());
                    fd.setValue(filename);
                }
                if (!Strings.isBlank(uv.getBackup())) {// 保存文件
                    if (uv.getBackup().startsWith("$707") || uv.getBackup().startsWith("$729")) {
                        String path = saveFile(cc, rc, uv, filename,
                                fv.getData());
                        if (!Strings.isBlank(uv.getPath())) {
                            FieldDo fd = rc.getField(uv.getPath());
                            fd.setValue(path);
                        }
                    } else if (!Strings.isBlank(uv.getPath())) {
                        FieldDo fd = rc.getField(uv.getPath());
                        fd.setValue(uv.getBackup());
                    }
                }
                rc.setValue("FILE_NAME", filename);
                if (!Strings.isBlank(uv.getAfter()))
                    doAfter(cc, rc, uv.getAfter());
            }
            cc.commitTx();
        } catch (Exception ex) {
            if (log.isErrorEnabled())
                log.error("upload", ex);
            cc.rollbackTx();
            if (ex instanceof EasyPlatformWithLabelKeyException)
                throw (EasyPlatformWithLabelKeyException) ex;
            throw new EasyPlatformWithLabelKeyException("upload.io.error",
                    ex.getMessage());
        } finally {
            cc.closeTx();
            IOUtils.closeQuietly(is);
        }
    }

    private void saveList(UploadVo uv, CommandContext cc, RecordContext rc) {
        int index = uv.getTarget().indexOf(":");
        String name = uv.getTarget().substring(index + 1);
        ListContext lc = cc.getWorkflowContext().getList(name);
        if (lc == null)
            throw new EasyPlatformWithLabelKeyException("datalist.not.found",
                    name);
        FileVo file = uv.getFiles().get(0);
        String ext = FilenameUtils.getExtension(file.getFileName());
        if (!Strings.isBlank(uv.getFormat()))
            ext = uv.getFormat();
        if (ext.toLowerCase().startsWith("xls") || ext.equalsIgnoreCase("et")) {// 目前仅支持EXCEL及WPS表格
            Transformer<Workbook, Map<String, List<List<FieldDo>>>> transformer = TransformerFactory
                    .createTransformer(Transformer.EXCEL2TABLE);
            transformer.setHandler(new Flating4ListHandler(cc, rc, lc, uv
                    .getLogic(), uv.getFirstRowNum(), uv.getFirstColNum(), uv
                    .getLastRowNum(), uv.getLastColNum()));
            InputStream is = null;
            try {
                for (FileVo fv : uv.getFiles()) {
                    if (!Strings.isBlank(uv.getBefore()))
                        doBefore(cc, fv, rc, uv.getBefore());
                    is = new ByteArrayInputStream(fv.getData());
                    Parameter parameter = new Parameter();
                    if (Strings.isBlank(uv.getSheetNames())) {
                        parameter.setSheets(new String[]{"1"});
                    } else if (!"*".equals(uv.getSheetNames())) {
                        parameter.setSheets(uv.getSheetNames().split(","));
                    }
                    parameter.setColumnsRow(uv.getColumnsRow());
                    parameter.setProgress(uv.isProgress());
                    parameter.setCountOfThread(uv.getCountOfThread());
                    if (ext.equalsIgnoreCase("xls")
                            || ext.equalsIgnoreCase("et"))// 97~2007
                        transformer.transform(
                                new HSSFWorkbook(is), parameter);
                    else
                        // 2010及以上
                        transformer.transform(
                                new XSSFWorkbook(is), parameter);
                    String filename = fv.getFileName();
                    if (uv.isObfuscation())
                        filename = RandomStringUtils.random(32, true, true)
                                + "." + FilenameUtils.getExtension(filename);
                    if (!Strings.isBlank(uv.getFilename())) {// 保存文件名称
                        FieldDo fd = rc.getField(uv.getFilename());
                        fd.setValue(filename);
                    }
                    if (!Strings.isBlank(uv.getBackup())) {// 保存文件
                        if (uv.getBackup().startsWith("$707") || uv.getBackup().startsWith("$729")) {
                            String path = saveFile(cc, rc, uv, filename,
                                    fv.getData());
                            if (!Strings.isBlank(uv.getPath())) {
                                FieldDo fd = rc.getField(uv.getPath());
                                fd.setValue(path);
                            }
                        } else if (!Strings.isBlank(uv.getPath())) {
                            FieldDo fd = rc.getField(uv.getPath());
                            fd.setValue(uv.getBackup());
                        }
                    }
                    rc.setValue("FILE_NAME", filename);
                    if (!Strings.isBlank(uv.getAfter()))
                        doAfter(cc, rc, uv.getAfter());
                }
            } catch (IOException ex) {
                log.error("upload", ex);
                throw new EasyPlatformWithLabelKeyException("upload.io.error",
                        ex.getMessage());
            } finally {
                IOUtils.closeQuietly(is);
            }
        }
    }

    private void doBefore(CommandContext cc, FileVo file, RecordContext rc,
                          String logic) {
        rc.setVariable(new FieldDo("FILE_NAME", FieldType.VARCHAR, file.getFileName()));
        String code = RuntimeUtils.eval(cc, logic, rc);
        if (!code.equals("0000"))
            throw new ScriptRuntimeException(code, cc.getMessage(code, rc));
    }

    private void doAfter(CommandContext cc, RecordContext rc, String logic) {
        String code = RuntimeUtils.eval(cc, logic, rc);
        if (!code.equals("0000"))
            throw new ScriptRuntimeException(code, cc.getMessage(code, rc));
    }

    private String saveField(UploadVo uv, CommandContext cc, RecordContext rc) {
        int index = uv.getTarget().indexOf(":");
        String name = uv.getTarget().substring(index + 1);
        if (rc.getParameterAsChar("814") != 'C')
            rc.setParameter("814", 'U');
        rc.setParameter("815", Boolean.TRUE);
        FileVo fv = uv.getFiles().get(0);
        FieldDo target = rc.getField(name);
        String filename = fv.getFileName();
        if (fv.getData() != null) {
            if (target.getType() == FieldType.BLOB)
                target.setValue(fv.getData());
            else if (target.getType() == FieldType.CLOB) {
                try {
                    target.setValue(new String(fv.getData(), uv.getCharset()));
                } catch (UnsupportedEncodingException e) {
                }
            }
            if (uv.isObfuscation())
                filename = RandomStringUtils.random(32, true, true) + "."
                        + FilenameUtils.getExtension(filename);
        }
        if (!Strings.isBlank(uv.getFilename())) {// 保存文件名称
            FieldDo fd = rc.getField(uv.getFilename());
            fd.setValue(filename);
        }
        if (!Strings.isBlank(uv.getPath())) {
            FieldDo fd = rc.getField(uv.getPath());
            fd.setValue(uv.getBackup());
        }

        fv.setFileName(filename);

        if (!Strings.isBlank(uv.getBefore()))
            doBefore(cc, fv, rc, uv.getBefore());


        if (!Strings.isBlank(uv.getBackup())) {// 保存文件
            String path = null;
            if (uv.getBackup().startsWith("$707") || uv.getBackup().startsWith("$729")) {
                path = saveFile(cc, rc, uv, filename, fv.getData());
                if (!Strings.isBlank(uv.getPath())) {
                    FieldDo fd = rc.getField(uv.getPath());
                    fd.setValue(path);
                }
            } else
                path = uv.getBackup() + "/" + fv.getFileName();
            if (target.getType() == FieldType.VARCHAR)
                target.setValue(path);
            if (!Strings.isBlank(uv.getAfter()))
                doAfter(cc, rc, uv.getAfter());
            return path;
        }
        return null;
    }

    private String saveFile(CommandContext cc, RecordContext rc, UploadVo uv, String filename,
                            byte[] data) {
        String path = uv.getBackup();
        if (path.indexOf("&dt") >= 0) {
            String format = cc.getProjectService().getConfig().getDateFormat();
            path = path.replace("&dt", DateFormatUtils.format(new Date(), format));
        }
        if (path.indexOf("&user") >= 0)
            path = path.replace("&user", cc.getUser().getId());
        String file = cc.getRealPath(path);
        OutputStream os = null;
        try {
            File folder = new File(FilenameUtils.normalize(file));
            if (!folder.exists())
                FileUtils.forceMkdir(folder);
            StringBuilder sb = new StringBuilder();
            sb.append(file).append("/").append(filename);
            file = FilenameUtils.normalize(sb.toString());
            os = new BufferedOutputStream(new FileOutputStream(file));
            IOUtils.write(data, os);
            rc.setVariable(new FieldDo("_NAME_", FieldType.VARCHAR, filename));
            rc.setVariable(new FieldDo("_PATH_", FieldType.VARCHAR, path));
            rc.setVariable(new FieldDo("_FILE_", FieldType.VARCHAR, file));
            if (uv.getThumbnails() != null) {//缩略图
                for (int i = 0; i < uv.getThumbnails().length; i++) {
                    String[] wh = StringUtils.split(uv.getThumbnails()[i], ",");
                    int w = Nums.toInt(wh[0], -1);
                    int h = Nums.toInt(wh[1], -1);
                    Images.zoomScale(file, folder.getAbsolutePath() + File.separatorChar + "t" + i + "_" + filename, w, h, null);
                }
                return FilenameUtils.normalize(path + "/t0_" + filename);
            }
        } catch (Exception e) {
            log.error("saveFile", e);
            throw new EasyPlatformWithLabelKeyException(
                    "script.engine.cmd.file", filename);
        } finally {
            IOUtils.closeQuietly(os);
        }
        return FilenameUtils.normalize(path + "/" + filename);
    }

}
